console.time "Start Server"
http = require "http"
url = require "url"
fs = require "fs"
path = require "path"
formidable = require "formidable"
global.co = require "co" if !global.co
co = global.co
ResFile = require "./ResFile"
config = require "../config"

webRegExp = new RegExp "^\\/web\\/.*","gm"

srvFactory = {}
exports.srvFactory = srvFactory
secLong = config.sessionTimeout
secLong = 1800 if secLong is undefined or secLong is 0
secLong *= 1000

sessionIdOp = config.sessionId

srvFactory._srvClz = (reqOpt,key,_sid,req,res,ref_time)->
  reqOpt.req = req
  reqOpt.res = res
  clzs = key.split "||"
  reqOpt.srvPath = clzs[clzs.length-1]
  reqOpt.srvKey = clzs[0]
  srvPath = clzs[clzs.length-1].replace /\./gm,"/"
  reqOpt._sid = _sid
  if _sid and srvFactory[_sid] and srvFactory[_sid][clzs[0]]
    srv = srvFactory[_sid][clzs[0]]
    return srv
  throw new Error key if !clzs[0]
  srvName = path.basename srvPath
  clazz = require("../srv/"+srvPath)[srvName]
  throw new Error srvPath+" The Class name is not correct! It may be "+path.basename srvPath if !clazz
  srv = new clazz()
  srv._srvFactory = srvFactory
  if _sid and srvFactory[_sid]
    srvFactory[_sid][clzs[0]] = srv
    srv.session = srvFactory[_sid].session
    srv._srvClz = (key)-> yield srvFactory._srvClz reqOpt,key,_sid,req,res,ref_time
  yield srv.onDraw() if srv.onDraw
  srv

server = http.createServer((req,res)->
  co(->
    #_sid sessionId,conn2 事务对应的连接,isTran是否开启事务,action执行完方法之后,向浏览器返回的命令
    #res_auto_end是否自动执行res.end(), _uid用于标识临时表,tmpTableArr已经创建的临时表的名字
    #reqOpt_ibmkx5s8为true则认为是reqOpt对象
    reqOpt = {_sid:undefined,_uid:undefined,tmpTableArr:[],req:req,res:res,res_auto_end:true,isTran:false,conn2:undefined,action:undefined,_private:undefined,srvPath:undefined,srvKey:undefined,mdStr:undefined,pms:undefined,reqOpt_ibmkx5s8:true}
    headers = req.headers
    method = req.method
    urlParse = url.parse req.url,true
    if method is "GET" or method is "get"
      parms = urlParse.query
      parms = {} if parms is null
    else if method is "POST" or method is "post"
      parms = urlParse.query
      parms = {} if parms is null
      form = new formidable.IncomingForm()
      form.uploadDir = _PROJECT_PATH+"/tmp"
      form.on "field",(field, value)->
        if form.type is "multipart"
          if parms[field] isnt undefined
            parms[field] = [parms[field]] if typeOf(parms[field]) isnt "array"
            parms[field].push value
            return
        if field is "pms"
          parms[field] = JSON.decode value
        else
          parms[field] = value
        return
      form.on "file",(field,file)->
        parms.pms = [] if parms.pms is undefined or parms.pms is null
        parms.pms[field] = file
        return
      formPmi = new Promise (resolve,reject)->
        form.on "error",(err)->
          reject err
          return
        form.on "end",->
          resolve()
          return
        return
      form.parse req
      yield formPmi
    else
      res.writeHead 404
      res.write method
      res.end()
      return
    ph = path.normalize(decodeURI(urlParse.pathname)).replace /\\/gm,"/"
    ph = config.mapping ph,req if config.mapping isnt undefined
    hdv = undefined
    if headers.hasOwnProperty "v"
      hdv = headers["v"]
    else
      hdv = parms._v if parms isnt null
    if hdv isnt "v"
      webRegExp.lastIndex = 0
      if webRegExp.test ph
        yield ResFile.resFile ph,req,res
      else
        res.writeHead 404
        res.end()
      return
    res.setHeader "Cache-Control","no-cache"
    res.setHeader "Pragma","no-cache"
    res.setHeader "Content-Type","application/json;charset=utf-8"
    phs = ph.split "/"
    throw new Error ph if phs.length < 3
    mdStr = phs[2]
    pms = null
    if typeOf(parms.pms) is "string"
      try
        pms = JSON.decode parms.pms
      catch err
        console.error err
        throw err
    else
      pms = parms.pms
    srv = undefined
    #获得_sid
    _sid = undefined
    if sessionIdOp is undefined or sessionIdOp is "js"
      _sid = parms._sid
    else if sessionIdOp is "header"
      _sid = headers["_sid"]
    else if sessionIdOp is "cookie" and headers.cookie
      cookieArr = headers.cookie.split ";"
      for item in cookieArr
        itemArr = item.split "="
        key = itemArr[0]
        if key is "_sid"
          _sid = itemArr[1]
          break
    _sid = parms._sid if !_sid
    
    if _sid and srvFactory[_sid] and parms.ref_time isnt "false"
      session = srvFactory[_sid].session
      if session._reqNum is 0
        clearTimeout session._sess_time
        session._sess_time = undefined
      session._reqNum++
    
    srv = yield srvFactory._srvClz reqOpt,phs[1],_sid,req,res,parms.ref_time
    
    #执行srv的方法
    err = undefined
    tk = undefined
    try
      tk = yield srv._applyMd reqOpt,mdStr,pms
    catch er
      #回滚事务
      if !er.fatal
        yield srv.rollbackTran reqOpt
      else
        srv.endConn reqOpt
      err = er
    if _sid and srvFactory[_sid] and parms.ref_time isnt "false"
      session = srvFactory[_sid].session
      session._reqNum--
      if session._reqNum is 0
        session._sess_time = setTimeout(->
          co srv._clearSession reqOpt
          return
        ,secLong)
    throw err if err
    if reqOpt.res_auto_end
      action = reqOpt.action
      drStr = undefined
      if action
        drStr = JSON.encode(action:action,d:tk)
      else
        drStr = JSON.encode(d:tk)
      if mdStr is "_sessionId"
        srvFactory[tk] = {}
        srvFactory[tk].session = {}
        srvFactory[tk].session._reqNum = 0
        srvFactory[tk].session._sess_time = setTimeout(->
          co srv._clearSession reqOpt
          return
        ,secLong)
      gf = yield ResFile.gzipFn headers["accept-encoding"],"json",new Buffer drStr
      ResFile.md5Etag gf.zipBuf,gf.zipType,req,res
    return
  ).catch((err)->
      if !err.is_out
        if err.stack
          console.error err.stack
        else
          console.error err
      err.is_out = true
      error = err.toString()
      error = JSON.encode err if error is "[object Object]"
      res.write JSON.encode {error:error}
      res.end()
      return
  )
  return
)
server.on "connection",(socket)->
  socket.setNoDelay true
  return
process.on "uncaughtException",(err)->
  console.error err if console.error err
  console.error err.stack if console.error err.stack
  return
server.listen config.port or 80
console.log "http://localhost:" + config.port or 80
exports.server = server
console.timeEnd "Start Server"
